/*

Gamebots UT Copyright (c) 2002, Andrew N. Marshal, Gal Kaminka
Gamebots UT 2003/2004 port Copyright (c) 2005
       Joe Manojlovich, Tim Garwood, Jessica Bayliss
Gamebots Pogamut derivation Copyright (c) 2007, Michal Bida
	- derived mainly from 2002 Marshal, Kaminka version

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software must also be in compliance with the Epic Games Inc. license for mods which states the following: "Your mods must be distributed solely for free, period. Neither you, nor any other person or party, may sell them to anyone, commercially exploit them in any way, or charge anyone for receiving or using them without prior written consent of Epic Games Inc. You may exchange them at no charge among other end-users and distribute them to others over the Internet, on magazine cover disks, or otherwise for free." Please see http://www.epicgames.com/ut2k4_eula.html for more information.

*/


// This is the class, where we add additional functionality to Player HUD.
// We draw on the HUD additional debug information about bots and player/s.
class GBHud extends HudBase
	config(BotAPI);

//Maximum distance of navigation point from us, when we still be drawing its name
var float NavPointBeaconDrawDistance;

//How many points we should shift in y coordinate to write next line on the HUD
//properly
var float shift;

//var config bool bDisplayRoute;

//Some variables for setting what should be visible on the HUD
var bool bDisplayMyLocation;
var bool bDrawNavPointsNames;
var bool bDisplayDebug;
var bool bDisplayHelp;
var bool bDisplayInformation;
var bool bDisplayPlayerList;
var bool bDisplayRoute;
var bool bDisplayHealthBar;
var bool bDisplayTextBubble;
var int DisplayPlayerPositions;


/* This is our entry point function. This function is called periodically (from
interaction class).
*/
simulated function PostRender(Canvas C)
{
	local float XPos, YPos;

	SetFont(C,0,155,55);

	XPos = 25;
	YPos = 25;

	if (bDrawNavPointsNames)
		DrawNavPointsNames(C);

	if (bDisplayHelp)
		DrawHelp(C,XPos,YPos);

	if (bDisplayMyLocation)
		DrawMyLocation(C, XPos, YPos);

	if (bDisplayPlayerList)
		DrawPlayerList(C, XPos, YPos);

	if (DisplayPlayerPositions >= 1)
		DrawPlayerPositions(C);

	//This have to be last - large ammount of text written
	if (bDisplayInformation)
		DrawInformation(C, XPos, YPos);

	if (bDisplayRoute)
		DrawRoute();

    //Draw3DLine(PlayerOwner.ViewTarget.Location + Pawn(PlayerOwner.ViewTarget).BaseEyeHeight * vect(0,0,1),PlayerOwner.FocalPoint,class'Canvas'.Static.MakeColor(255,0,0));

	//DrawNavPointsGrid(C);

	//PlayerOwner.ViewTarget.DisplayDebug(C, XPos, YPos);


}
/*
function PostBeginPlay()
{
	super.PostBeginPlay();
	PlayerOwner.myHUD = self; //why is this necesssary?
}
*/


//Here we are setting the size of the font according to the resolution
function SetFont(Canvas Canvas, int red, int green, int blue)
{
	local float XL;

	Canvas.Font = GetConsoleFont(Canvas);
	Canvas.Style = ERenderStyle.STY_Alpha;
    Canvas.DrawColor = class'Canvas'.Static.MakeColor(red,green,blue);
    Canvas.StrLen("TEST", XL, shift);

	if (Canvas.SizeX >= 1024 )
	{
		Canvas.Font = Canvas.MedFont;
		Canvas.FontScaleX=1;
		Canvas.FontScaleY=1;
	    Canvas.StrLen("TEST", XL, shift);
	}

}


simulated function DrawLine(vector Start, vector End)
{
	if (ROLE == ROLE_AUTHORITY)
		return;
	Draw3DLine(Start,End,class'Canvas'.Static.MakeColor(255,0,0));
}

//Will display Help (keys for GB and/or HUD control)
function DrawHelp(Canvas C, out float ScreenLocX, out float ScreenLocY)
{
	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("GameBots 2004 HUD Help:",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + H - Enables/Disables this help",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + I - Enables/Disables additional info (about reachability GRID, etc.)",true);
	ScreenLocY += shift;

    C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + M - Enables/Disables my location and rotation info.",true);
	ScreenLocY += shift;

   	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + N - Enables/Disables NavPoint names.",true);
	ScreenLocY += shift;

   	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + '[' or ']' - Increase/Decrease range of drawing NavPoint names.",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + G - Enables/Disables reachability GRID.",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + L - Enables/Disables Player List.",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + P - Enables/Disables displaying player positions.",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + R - Enables/Disables route drawing (when spectating the bot)",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + B - Enables/Disables HealthBar",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + U - Enables/Disables text bubbles",true);
	ScreenLocY += shift;

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("ALT + D - Enables/Disables debug information",true);
	ScreenLocY += shift;
}

//Some comment to GBHUD will be written
function DrawInformation(Canvas C, out float ScreenLocX, out float ScreenLocY)
{

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("Reachability grid now has oriented edges. Colours info: if"$
	" one of the flags is R_PROSCRIBED or R_PLAYERONLY or R_FLY the colour of the"$
	" edge will be red regardless of any other flags. if the flag is R_JUMP and R_SPECIAL"$
	" the colour will be white. if the flag is R_JUMP the colour will be yellow."$
	" if the flag is not R_JUMP and if it is R_SPECIAL the colour will be blue."$
	" if none of these conditions were fullfilled yet and the flag is R_DOOR,"$
	" R_LADDER or R_SWIM the colour will be black. In other cases (flag can be"$
	" R_WALK or R_FORCED) the colour will be green. ",true);
	//ScreenLocY += shift;

}

//Will draw our current location, rotation and velocity on the HUD
function DrawMyLocation(canvas C, out float ScreenLocX, out float ScreenLocY)
{
	local vector PlayerLocation, PlayerVelocity;
	local rotator myRotation;
	local string PlayerRotation;

	//If we currently control Pawn, we will take its coordinates
	if (PlayerOwner.Pawn != none)
	{
		PlayerLocation = PlayerOwner.Pawn.Location;
		myRotation = PlayerOwner.Pawn.Rotation;
		//The ViewPitch is something else then Pawn rotation!!!
		myRotation.Pitch = int(PlayerOwner.Pawn.ViewPitch) * 65556/255;
		PlayerRotation = string(myRotation);
		PlayerVelocity = PlayerOwner.Pawn.Velocity;
	}
	//If are spectating someone, we will put his coordinates
	else if (PlayerOwner.ViewTarget != none)
	{
		PlayerLocation = PlayerOwner.ViewTarget.Location;
		PlayerRotation = string(PlayerOwner.ViewTarget.Rotation);
		PlayerVelocity = PlayerOwner.ViewTarget.Velocity;
	}
	//Otherwise put coordinates of the Controller class (we are spectating now)
	//don't have the body
	else
	{
		PlayerLocation = PlayerOwner.Location;
		PlayerRotation = string(PlayerOwner.Rotation);
		PlayerVelocity = PlayerOwner.Velocity;
	}

	C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("My Location: "$PlayerLocation$" My Rotation:"$PlayerRotation$" My Velocity:"$PlayerVelocity,true);
	ScreenLocY += shift;
}

//This function is called from each GBxPawn, when we may see him - this doesnt mean
//that we actually see him, when the function is called
function NotifySeePawn(Canvas C, GBxPawn P, float ScreenLocX, float ScreenLocY)
{
 	//TODO: Check also distance?
	if (!PlayerOwner.LineOfSightTo(P))
		return;

	if (bDisplayTextBubble)
		DrawTextBubble(C, P, ScreenLocX, ScreenLocY);
	if (bDisplayHealthBar)
		DrawHealthBar(C, P);
}

//Will draw the text bubble containing the last string the bot was sending to communicate
function DrawTextBubble(Canvas C, GBxPawn P, float ScreenLocX, float ScreenLocY)
{
	local float XL,YL;

	if ( Level.TimeSeconds < (P.TextBubbleSetTime + P.TextBubbleFadeOut) )
	{
		SetFont(C,0,55,155);
		C.StrLen(P.TextBubble, XL, YL);
		C.SetPos(ScreenLocX - 0.5*XL , ScreenLocY - YL);
		C.DrawText(P.TextBubble,true);
	}
}

//Will draw the bots current health ammount using text and health bar
function DrawHealthBar(Canvas C, GBxPawn P)
{
	local texture HealthTex;
	local vector PawnLocation, CanvasBarEndLocation, CanvasHealthTexLocation;
	local float BarLength;

	HealthTex = Texture'engine.WhiteSquareTexture';
	if ( HealthTex == None)
		return;

	PawnLocation = P.Location;
	//We will draw HealthTex a little bit higher
	PawnLocation.z += 20;
    CanvasHealthTexLocation = C.WorldToScreen(PawnLocation);
	C.SetPos(CanvasHealthTexLocation.x, CanvasHealthTexLocation.y - 10);
	SetFont(C,155,0,0);
	C.DrawText(P.Health $ "%",true);

    //Health bar will be 100 ut units big
    //We want to scale the bar according to the distance
	PawnLocation.z -= 100;
	CanvasBarEndLocation = C.WorldToScreen(PawnLocation);
	BarLength = CanvasBarEndLocation.y - CanvasHealthTexLocation.y;

	C.SetPos( CanvasHealthTexLocation.x, CanvasHealthTexLocation.y );

	//First we will draw white bar showing 100 health
	SetFont(C,255,255,255);
	C.DrawTile( HealthTex, 6, BarLength, 0, 0, HealthTex.USize, HealthTex.VSize );

	//Then prepare everything for the second red health bar
	C.SetPos( CanvasHealthTexLocation.x,  CanvasHealthTexLocation.y );
	SetFont(C,155,0,0);

	//We have to do this so (division later in DrawTile fc) because we loose
	//everything behind . because of replication (all floats truncated)
	BarLength = P.Health * BarLength;
	if (P.Health > 0)
		C.DrawTile( HealthTex, 6, BarLength / 100, 0, 0, HealthTex.USize, HealthTex.VSize );

}

/*  Will draw some debug information for each bot or player in the game
	The direction of all the bots with the distance from us will be drawn (even if they
	are not visible - good if we want to find them in the map).
	Then the speed vector also with current bot focus and his field of view will be
	drawn by the lines of different color.

*/
function DrawPlayerPositions(Canvas Canvas)
{
	local GBReplicationInfo MyRepInfo;
	//some needed vectors
	local vector ViewLocation, PawnVelocity, PawnPosition, resNormal, resVect;
	//some vectors for canvas position counting
	local vector FirstLine, SecondLine, CanvasPawnPosition, CanvasPawnVelocity, CanvasPawnFocus, CanvasFOVLimit;

	local string PlayerName, FocusName;
	local rotator ViewRotation, fovLimit, PawnRotation;
	local int bIsCamera;
	local float XL, YL;

	//
    GetPlayerViewInformation(ViewLocation, ViewRotation, bIsCamera );

	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo)
	{
		//We want to show here just relevant information
		if (!MyRepInfo.PawnIsNone() && (MyRepInfo.MyPRI != PlayerOwner.PlayerReplicationInfo) )
		{
			/*if ( (PlayerOwner.ViewTarget != none) && (PlayerOwner.ViewTarget.Controller != none) && (PlayerOwner.ViewTarget.Controller.PlayerReplicationInfo != none ) && (PlayerOwner.ViewTarget.Controller.PlayerReplicationInfo == MyRepInfo.MyPRI) )
			{
				continue;
			}*/
			PawnPosition = MyRepInfo.GetLocation();

			if (inFront(PawnPosition, ViewLocation, ViewRotation, bIsCamera))
			{

				PlayerName = MyRepInfo.MyPRI.GetHumanReadableName();

				CanvasPawnPosition = Canvas.WorldToScreen(PawnPosition);

				PawnVelocity = MyRepInfo.GetVelocity();
				CanvasPawnVelocity = Canvas.WorldToScreen(PawnPosition + PawnVelocity);

				//Add information about distance and PlayerName
				PlayerName = VSize(PawnPosition - ViewLocation) $ " " $ PlayerName;
				Canvas.StrLen(PlayerName, XL, YL);
				Canvas.SetPos(CanvasPawnPosition.x - 0.5*XL , CanvasPawnPosition.y - YL);
				Canvas.DrawText(PlayerName,true);

                DrawCanvasLine(CanvasPawnPosition.X, CanvasPawnPosition.Y, CanvasPawnVelocity.X, CanvasPawnVelocity.Y, class'Canvas'.Static.MakeColor(255,0,0));

				//We don't want to draw arrow if the velocity is too small...
				if ( VSize( (Normal(PawnVelocity) * 7) ) < VSize( PawnVelocity ) )
				{
                	resNormal = Normal(PawnVelocity cross vect(0, 0, 1));
					resVect = (PawnPosition + PawnVelocity) - (Normal(PawnVelocity) * 7);

					FirstLine = Canvas.WorldToScreen(resVect + resNormal * 3);
					SecondLine = Canvas.WorldToScreen(resVect - resNormal * 3);

					DrawCanvasLine(FirstLine.x, FirstLine.y, CanvasPawnVelocity.x, CanvasPawnVelocity.y,class'Canvas'.Static.MakeColor(255,0,0));
					DrawCanvasLine(SecondLine.x, SecondLine.y, CanvasPawnVelocity.x, CanvasPawnVelocity.y,class'Canvas'.Static.MakeColor(255,0,0));
				}
				//Draw3DLine(PawnPosition,PawnPosition + MyRepInfo.GetVelocity(),class'Canvas'.Static.MakeColor(255,0,0));


				if (DisplayPlayerPositions >= 2)
				{
					//DrawFocus
					CanvasPawnFocus = Canvas.WorldToScreen(MyRepInfo.GetFocus());
					FocusName = MyRepInfo.GetFocusName();

        	        DrawCanvasLine(CanvasPawnPosition.X, CanvasPawnPosition.Y, CanvasPawnFocus.X, CanvasPawnFocus.Y, class'Canvas'.Static.MakeColor(255,255,255));
            	    if (FocusName != "")
                	{
	                	Canvas.StrLen(FocusName, XL, YL);
						Canvas.SetPos(CanvasPawnFocus.x - 0.5*XL , CanvasPawnFocus.y - YL);
						Canvas.DrawText(FocusName,true);
					}

					//DrawFOV - approx right now
					fovLimit.pitch = 0;
					fovLimit.Yaw = ((107 / 2) * 182.1);
					fovLimit.Roll = 0;

					PawnRotation = MyRepInfo.GetRotation();

					//First FOV line
					CanvasFOVLimit = Canvas.WorldToScreen( PawnPosition + vector(PawnRotation - fovLimit) * 300 );
	       	        DrawCanvasLine(CanvasPawnPosition.X, CanvasPawnPosition.Y, CanvasFOVLimit.X, CanvasFOVLimit.Y, class'Canvas'.Static.MakeColor(255,255,0));

					//Second FOV line
					CanvasFOVLimit = Canvas.WorldToScreen( PawnPosition + vector(PawnRotation + fovLimit) * 300 );
	       	        DrawCanvasLine(CanvasPawnPosition.X, CanvasPawnPosition.Y, CanvasFOVLimit.X, CanvasFOVLimit.Y, class'Canvas'.Static.MakeColor(255,255,0));

				}
			}
		}
	}




}

/* Will go through all GBReplicationInfo classes and will display the information
	about players currently on the server.
*/
function DrawPlayerList(canvas C, out float ScreenLocX, out float ScreenLocY)
{
	local GBReplicationInfo MyRepInfo;

    C.SetPos(ScreenLocX, ScreenLocY);
	C.DrawText("Player List: ",true);

	foreach DynamicActors(Class'GBReplicationInfo', MyRepInfo)
	{
		//We want to show here just relevant information
		if (!MyRepInfo.PawnIsNone())
		{
			ScreenLocY += shift;
			C.SetPos(ScreenLocX, ScreenLocY);
			C.DrawText("Name: "$MyRepInfo.MyPRI.GetHumanReadableName() $
				" Location: "$MyRepInfo.GetLocation() $
				" Rotation: "$MyRepInfo.GetRotation() $
				" Velocity: "$MyRepInfo.GetVelocity(),true);
		}
	}

	ScreenLocY += shift;
}

//Will go through all navigation points in the level and find those we can see
function DrawNavPointsNames(canvas Canvas)
{
	local NavigationPoint N;
	local vector CanvasPosition, ObserverPosition, ViewLocation;
	local rotator ViewRotation;
	local int bIsCamera;

	GetPlayerViewInformation(ViewLocation, ViewRotation, bIsCamera );
	for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
	{
		if( PlayerOwner.FastTrace(N.Location, ViewLocation) && inFront(N.Location, ViewLocation, ViewRotation, bIsCamera))
		{
			CanvasPosition = Canvas.WorldToScreen(N.Location);
			DrawNavPointName(Canvas,N,ViewLocation,CanvasPosition.X,CanvasPosition.Y);
		}
	}
}

//This will draw the game Id of the NavPoint if it is in the drawing distance
function DrawNavPointName(Canvas C, NavigationPoint N, vector ObserverPosition, float ScreenLocX, float ScreenLocY)
{

	local float XL,YL,floatDist;
	local vector distance;

	distance = N.Location - ObserverPosition;
	floatDist = sqrt(square(N.Location.x - ObserverPosition.x) + square(N.Location.y - ObserverPosition.y) + square(N.Location.z - ObserverPosition.z));

	if (floatDist > NavPointBeaconDrawDistance)
		return;

	C.StrLen(string(N), XL, YL);
	C.SetPos(ScreenLocX - 0.5*XL , ScreenLocY - YL);
	C.DrawText(string(N),true);
	//log("Drawing: "$string(N));
}

/* Returns our current view - camera location, rotation and bool (by int) bIsCamera if we are currently
	spectating and don't have any player selected.
*/
function GetPlayerViewInformation(out vector CameraLocation, out rotator CameraRotation, out int bIsCamera )
{
	if (PlayerOwner.Pawn != none)
	{
		CameraLocation = PlayerOwner.Pawn.Location;
		CameraRotation = PlayerOwner.Pawn.Rotation;
		CameraRotation.Pitch = int(PlayerOwner.Pawn.ViewPitch) * 65556/255;
		bIsCamera = 0;
	}
	else if (PlayerOwner.ViewTarget != none)
	{
		CameraLocation = PlayerOwner.CalcViewLocation;
		CameraRotation = PlayerOwner.CalcViewRotation;
		bIsCamera = 1;
	}
	else
	{
		CameraLocation = PlayerOwner.Location;
		CameraRotation = PlayerOwner.Rotation;
		bIsCamera = 0;
	}
}

//Should tell us if the vector A is in our Field Of View according to the Camera location and rotation
function bool inFront(Vector A, vector CameraLocation, rotator CameraRotation, int bIsCamera)
{
	local rotator angle;
    local float visionLimit;

	//TODO: Get correct FOV, how?
	//visionLimit = cos( PlayerOwner.FovAngle / 2 );
	visionLimit = 0.707;

	//can't be infront if inside bot
   	if( VSize(CameraLocation - A) < CollisionRadius)
   		return false;

	angle = ( CameraRotation - rotator(A - CameraLocation) );


	if( (cos(angle.Yaw / 10435.0) < visionLimit) ||
		(cos(angle.Pitch / 10435.0) < visionLimit) )
		return false;
	else
		return true;


}

defaultproperties
{
     NavPointBeaconDrawDistance=500.000000
     bDisplayMyLocation=True
     bDrawNavPointsNames=True
     bDisplayHelp=True
     bDisplayPlayerList=True
     bDisplayTextBubble=True
}
